package cn.edu.njuit.webserver.securityserver.filter;

import cn.edu.njuit.webserver.securityserver.common.Result;
import cn.edu.njuit.webserver.securityserver.entity.Role;
import cn.edu.njuit.webserver.securityserver.service.TokenService;
import cn.edu.njuit.webserver.securityserver.service.dto.UserToken;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.exceptions.JWTVerificationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.List;

import static cn.edu.njuit.webserver.securityserver.common.ResultCode.TOKEN_EXPIRED;

/**
 * @Description: jwt
 * @Data: 2022/9/6 15:20
 * @Author: guzy
 */
@Configuration
public class JwtApiFilter extends GenericFilterBean {
    @Autowired
    @Qualifier("JwtTokenService")
    TokenService tokenService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //1.从请求头不找到令牌
        String token = request.getHeader("Authorization");
        if (token == null) { //如果请求头没有，从查询0.0参数中找到令牌
            token = request.getParameter("Authorization");
        }
        if (token != null) {
            //通过令牌返解析出令牌⽤户对象。
            //返解析过程中jwt⼯具类会校验格式、校验有效期。
            UserToken userToken = new UserToken();
            try {
                userToken = this.tokenService.getUser(token);
            } catch (Exception e) {
//                e.printStackTrace();
                logger.error("令牌不合法，不让访问：" + e.getMessage());
                Result result = new Result();
                result.setCode(TOKEN_EXPIRED.code());
                result.setSuccess(false);
                result.setMsg(TOKEN_EXPIRED.message());
                result.setData(e.getMessage());
                response.setContentType("application/json;charset=utf8");
                response.getWriter().write(JSONObject.toJSONString(result));
                return;
            }
            if (userToken != null) {
                //此处继续认证逻辑。
                //根据⽤户⻆⾊数组，创建security所需的权限列表authorities。
                List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                for (Role role : userToken.getRoles()) {
                    SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleName());
                    authorities.add(authority);
                }
                //构造为UserDetailsService⽤户对象
                User authenticatedUser = new User(userToken.getLogin(), "", authorities);
                //构造⼀个SpringSecurity授权对象Authentication
                //授权对象Authentication需包含3个要素：
                //1、⽤户对象：此处直接使⽤UserDetailsService⽤户对象
                //2、用户口令 密码
                //3、⽤户权限：⾄少赋予⼀个权限才能够被SpringSecurity视为已认证。
                Authentication user = new UsernamePasswordAuthenticationToken(authenticatedUser, token, authorities);
                SecurityContextHolder.getContext().setAuthentication(user);
            }
        }
        filterChain.doFilter(request, response);
    }
}
